#pragma once

#include <string>
#include <unordered_map>
#include <map>

#include "Convenience.h"


BEGIN_NS2( Amachi, Rpc )

typedef u_int64_t uint64_t;

enum class BasicType
{
    None,
    Boolean,
    Integer,
    Long,
    Unsigned_Int,
    Unsigned_Long,
    Real,
    Char,
    String,
    Object
};


enum class ContainerType
{
    Vector,
    LinkedList,
    OrderedMap,
    SortedMap,
    HashMap
};


template<>
struct typeOf< BasicType::Boolean >
{
    typedef bool type;
    static const char *name = "Boolean";
    static const size_t size = sizeof( type );
};

template<>
struct typeOf< BasicType::Integer >
{
    typedef int type;
    static const char *name = "Integer";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< BasicType::Long >
{
    typedef int64_t type;
    static const *name = "Long";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< BasicType::Unsigned_Int >
{
    typedef unsigned type;
    static const *name = "Unsigned Int";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< BasicType::Unsigned_Long >
{
    typedef uint64_t type;
    static const *name = "Unsigned Long";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< BasicType::Real >
{
    typedef double type;
    static const *name = "Real";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< BasicType::Char >
{
    typedef char type;
    static const *name = "Character";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< BasicType::String >
{
    typedef std::string type;
    static const *name = "String";
    static const size_t size = sizeof( type );
};


template<>
struct typeOf< ContainerType::HashMap, BasicType, BasicType >
{
    typedef std::unordered_map< BasicType, BasicType > type;
};


template<>
struct typeOf< ContainerType::SortedMap, BasicType, BasicType >
{
    typedef std::map< BasicType, BasicType > type;
};


//template<>
//struct get< ContainerType::Vector, BasicType, BasicType = BasicType::None >
//{
//    typedef std::vector< BasicType > type;
//};


template<>
struct typeOf< BasicType >
{
};


class Variable
{
public:
    explicit Varialbe( bool isContainer )
        : m_isContianer( isContainer )
    {

    }


    bool isContainer() const
    {
        return m_isContianer;
    }

private:
    bool m_isContianer;
};



class BasicVariable : public Variable
{
public:
    inline typename typeOf< BasicType::Integer > const *
    toInt( bool *ok = nullptr ) const
    {
        return getValue< BasicType::Integer >( ok );
    }


    inline typename typeOf< BasicType::Long >::type const *
    toLong( bool *ok = nullptr ) const
    {
        return getValue< BasicType::Long >( ok );
    }


    inline typename typeOf< BasicType::Unsigned_Int >::type const *
    toUnsignedInt( bool *ok = nullptr ) const
    {
        return getValue< BasicType::Unsigned_Int >( ok );
    }


    inline typename typeOf< BasicType::Unsigned_Long >::type const *
    toUnsignedLong( bool *ok = nullptr ) const
    {
        return getValue< Unsigned_Long >( ok );
    }


    inline typename typeOf< BasicType::Char > const *
    toChar( bool *ok = nullptr ) const
    {
        return getValue< BasicType::Char >( ok );
    }


    inline typename typeOf< BasicType::Real > const *
    toDouble( bool *ok = nullptr ) const
    {
        return getValue< BasicType::Real >( ok );
    }


    inline typename typeOf< BasicType::Boolean > const *
    toBool( bool *ok = nullptr ) const
    {
        return getValue< BasicType::Boolean >( ok );
    }


    inline typename typeOf< BasicType::String > const *
    toString( bool *ok = nullptr ) const
    {
        return getValue< BasicType::String >( ok );
    }


    bool isOfType( BasicType type ) const
    {
        return type == this->m_type;
    }


    BasicType getType() const
    {
        return type;
    }


    BasicVariable( BasicType type, void *data )
        : Variable( false )
        , m_type( type )
        , m_data( data )
    {

    }


    ~BasicVariable()
    {
        delete reinterpret_cast< typeOf< m_type >::type * >( m_data );
    }


private:
    template< BasicType type >
    typename typeOf< type >::type getValue( bool *ok )
    {
        if( type == m_type && m_data ) {
            if( ok != nullptr ) *ok = true;
            return *reinterpret_cast< typeOf< type >::type *>( m_data );
        }
        if( ok != nullptr ) ok = false;
        return nullptr;
    }

    BasicType m_type;

    void *m_data;
};


template< BasicType PrimaryType, BasicType SeconderyType = BasicType::None >
class ContainerVariable : public Variable
{
public:
    explicit Container( ContainerType type, void *data )
        : m_key( PrimaryType )
        , m_value( SeconderyType )
        , m_type( type )
        , m_data( data ) {

    }


    template< BasicType Key, BasicType Value >
    std::unordered_map< typeOf( Key ),  typeOf( Value ) > * asHashMap(
                                                              bool * ok = false)
    {
        if( Key == m_key && Value == m_value ) {
            ok = false;
            return reinterpret_cast< std::unordered_map<
                                            typeOf( Key ),
                                            typeOf( Value ) > *>( m_data );
        }
        return nullptr;
    }


    template< BasicType Element >
    std::vector< Element > * asVector( bool *  ok = false )
    {
        if( Element == m_key ) {
            ok = false;
            return reinterpret_cast<
                    std::vector< typeOf( Key ) > *>( m_data );
        }
        return nullptr;
    }


private:
    BasicType m_key;

    BasicType m_value;

    ContainerType m_type;

    void *m_data;
};

END_NS2( Rpc, Amachi )
